package com.example.rabbitmq.spring.config;

import com.example.rabbitmq.spring.adapter.MessageDelegate;
import com.example.rabbitmq.spring.constant.RabbitMqConstant;
import com.example.rabbitmq.spring.convert.TextMessageConverter;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.amqp.support.ConsumerTagStrategy;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author guo
 * @date 2021/11/23 19:37 星期二
 **/
@Configuration
@ComponentScan({"com.example.rabbitmq.spring.*"})
public class RabbitMqConfig {

    @Bean
    public ConnectionFactory connectionFactory(){

        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(RabbitMqConstant.ADDRESS);
        connectionFactory.setVirtualHost(RabbitMqConstant.VIRTUAL_HOST);

        connectionFactory.setUsername(RabbitMqConstant.USERNAME);
        connectionFactory.setPassword(RabbitMqConstant.PASSWORD);

        return connectionFactory;
    }

    /**
     * 针对消费者配置
     * 1. 设置交换机类型
     * 2. 将队列绑定到交换机
     FanoutExchange: 将消息分发到所有的绑定队列，无routingkey的概念
     HeadersExchange ：通过添加属性key-value匹配
     DirectExchange:按照routingkey分发到指定队列
     TopicExchange:多关键字匹配
     */
    @Bean
    public TopicExchange exchange001() {
        return new TopicExchange("topic001", true, false);
    }

    @Bean
    public Queue queue001() {
        return new Queue("queue001", true); //队列持久
    }

    @Bean
    public Binding binding001() {
        return BindingBuilder.bind(queue001()).to(exchange001()).with("spring.*");
    }

    @Bean
    public TopicExchange exchange002() {
        return new TopicExchange("topic002", true, false);
    }

    @Bean
    public Queue queue002() {
        return new Queue("queue002", true); //队列持久
    }

    @Bean
    public Binding binding002() {
        return BindingBuilder.bind(queue002()).to(exchange002()).with("rabbit.*");
    }

    @Bean
    public Queue queue003() {
        return new Queue("queue003", true); //队列持久
    }

    @Bean
    public Binding binding003() {
        return BindingBuilder.bind(queue003()).to(exchange001()).with("mq.*");
    }

    @Bean
    public Queue queue_image() {
        return new Queue("image_queue", true); //队列持久
    }

    @Bean
    public Queue queue_pdf() {
        return new Queue("pdf_queue", true); //队列持久
    }


    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){

        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }



    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        //这个类非常强大，我们可以对他做很多设置，对于消费者的配置项，这个类都可以满足
        //监听队列（多个队列）、自动启动、自动声明功能
        //可以设置事务特性、事务管理器、事务属性、事务容量（并发）、是否开启事务、回滚消息等
        //可以设置消费者数量、最大最小数量、批量消费
        //设置消息确认和自动确认模式、是否重回队列、异常捕获handler函数
        //设置消费者标签生成策略、是否独占模式、消费者属性等
        //设置具体的转换器、消息转换器等
        //很多基于RabbitMQ的自制定化后端管控台在进行动态配置的时候，也是根据这一特性去实现的。
        //注意：SimpleMessageListenerContainer可以进行动态设置，比如在运行中的应用可以动态
        //修改其消费者数量的大小、接收消息的模式等

        //同时监听多个队列
        container.setQueues(queue001(),queue002(),queue003(),queue_image(),queue_pdf());
        //设置当前消费者数量
        container.setMaxConcurrentConsumers(5);
        container.setConcurrentConsumers(1);
        //设置是否重回队列
        container.setDefaultRequeueRejected(false);
        //设置自动签收
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //设置消费端标策略
        container.setConsumerTagStrategy(queue -> queue + "_" + UUID.randomUUID().toString());

        //消息监听获取消息
//        container.setMessageListener((ChannelAwareMessageListener) (message, channel) -> {
//            System.out.println("消息："+new String(message.getBody()));
//            System.out.println("==================================");
//        });

        /*
         * //1.第一种适配器获取消息
         *         //defaultListenerMethod默认监听方法名称：用于设置监听方法名称
         *         //Delegate 委托对象：实际真实的委托对象，用于处理消息
         *         //queueOrTagToMethodName 队列标识与方法名称组成集合
         *         //可以一一进行队列与方法名称的匹配
         *         //队列和方法名称绑定，即指定队列里的消息会被绑定的方法所接受处理
         *         MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new MessageDelegate());
         *
         *         //自定义指定方法名称
         *         messageListenerAdapter.setDefaultListenerMethod("consumeMessage");
         *         //自定义方法参数转换
         *         messageListenerAdapter.setMessageConverter(new TextMessageConverter());
         *
         *         //MessageListenerAdapter是ChannelAwareMessageListener的实现继承类，可自定义监听实现
         *         container.setMessageListener(messageListenerAdapter);
         */


        /*
         * 2.适配器方式：我们的队列名称和方法名称也可以进行一一匹配

        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setMessageConverter(new TextMessageConverter());
        Map<String, String> queueOrTagToMethodName = new HashMap<>(16);
        queueOrTagToMethodName.put("queue001", "method1");
        queueOrTagToMethodName.put("queue002", "method2");
        adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);

        adapter.setMessageConverter(new TextMessageConverter());
        container.setMessageListener(adapter);
*/

        /**
         * 支持json格式的转换器
         *
         */
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");

        //声明出Json格式的转换器，然后设置进适配器中
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        adapter.setMessageConverter(jackson2JsonMessageConverter);

        return container;

    }


}
